package mongoUtils

import (
	"context"
	"fmt"
	"github.com/astaxie/beego/logs"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
)

func ParseMap(ms map[string]*any, filter *bson.M) *bson.M {
	for s, a := range ms {
		(*filter)[s] = bson.M{
			"$regex":   a,
			"$options": "im",
		}
	}
	return filter
}

func Find[T any](ctx context.Context, coll *mongo.Collection, filter any, opts ...*options.FindOptions) ([]*T, bool) {
	cursor, err := coll.Find(ctx, filter, opts...)
	var results []*T
	if err != nil {
		return nil, false
	}
	defer cursor.Close(ctx)
	if err = cursor.All(ctx, &results); err != nil {
		log.Fatal(err)
	}
	return results, true
}
func FindOne[T any](ctx context.Context, coll *mongo.Collection, filter any, opts ...*options.FindOneOptions) (*T, bool) {
	var result T
	err := coll.FindOne(ctx, filter, opts...).Decode(&result)
	if err != nil {
		return &result, false
	}
	return &result, true
}
func SelectPage[T any](ctx context.Context, coll *mongo.Collection, filter, sort any, skip, limit int64) (int64, []T, error) {
	resultCount, err := coll.CountDocuments(ctx, filter)
	if err != nil {
		return 0, nil, err
	}
	opts := options.Find().SetSort(sort).SetSkip(skip).SetLimit(limit)
	finder, err := coll.Find(ctx, filter, opts)
	if err != nil {
		return resultCount, nil, err
	}
	defer finder.Close(ctx)
	result := make([]T, 0)
	if err := finder.All(ctx, &result); err != nil {
		return resultCount, nil, err
	}
	return resultCount, result, nil
}

func UpdateOne[T any](ctx context.Context, coll *mongo.Collection, filter any, update *T) (int64, bool) {
	updateT := bson.M{"$set": update}
	result, err := coll.UpdateOne(ctx, filter, updateT, options.Update().SetUpsert(false))
	if err != nil {
		return 0, false
	}
	if result.MatchedCount == 0 {
		return 0, false
	}
	return result.MatchedCount, true
}
func UpdateMany[T any](ctx context.Context, coll *mongo.Collection, filter any, update []T) (int64, error) {
	result, err := coll.UpdateMany(ctx, filter, update, options.Update())
	if err != nil {
		return 0, err
	}
	if result.MatchedCount == 0 {
		return 0, fmt.Errorf("Update result: %s ", "document not found")
	}
	return result.MatchedCount, nil
}
func Delete[T any](ctx context.Context, coll *mongo.Collection, filter any) (int64, error) {
	result, err := coll.DeleteMany(ctx, filter, options.Delete())
	if err != nil {
		return 0, err
	}
	if result.DeletedCount == 0 {
		return 0, fmt.Errorf("DeleteOne result: %s ", "document not found")
	}
	return result.DeletedCount, nil
}
func CreateIndexes(ctx context.Context, coll *mongo.Collection, indexes []mongo.IndexModel) {
	str, err := coll.Indexes().CreateMany(ctx, indexes, options.CreateIndexes())
	if err != nil {
		panic(err)
	}
	fmt.Println(str)
}

func InsertOne[T any](ctx context.Context, coll *mongo.Collection, value *T, opts ...*options.InsertOneOptions) bool {
	_, err := coll.InsertOne(ctx, value, opts...)
	if err != nil {
		log.Fatal(err)
		return false
	}
	return true
}
func InsertMany[T any](ctx context.Context, coll *mongo.Collection, value []*T, opts ...*options.InsertOneOptions) {
	for _, v := range value {
		InsertOne[T](ctx, coll, v, opts...)
	}
}

// TransactionSomeThing 仅仅多节点使用
func TransactionSomeThing[T any](ctx context.Context, client *mongo.Client, coll *mongo.Collection, filter any, value any, handle TransactionHandle) {
	if err := client.UseSession(ctx, func(sessionContext mongo.SessionContext) error {
		//开启事务
		if err := sessionContext.StartTransaction(); err != nil {
			return err
		}
		//插入数据
		err := handle(sessionContext, coll, filter, value)
		if err != nil {
			return err
		}
		//提交事务
		return sessionContext.CommitTransaction(context.Background())
	}); err != nil {
		logs.Error("insert failed, err:%s", err.Error())

	}
}

type TransactionHandle func(sessionContext mongo.SessionContext, coll *mongo.Collection, filter any, value any) error

func Handle(ctx context.Context, coll *mongo.Collection, handle func(ctx context.Context, coll *mongo.Collection) bool) bool {
	return handle(ctx, coll)
	//coll.FindOneAndUpdate(ctx, filter, value, opts...).Decode(&result)

}
